package com.laycoding.layrpc.handler.client.handler;

import com.laycoding.layrpc.common.LayRpcRegistryCache;
import com.laycoding.layrpc.common.LayRpcRegistyCache;
import com.laycoding.layrpc.handler.client.LayRpcClientRegistry;
import com.laycoding.layrpc.handler.client.retry.RetryPolicyService;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.EventLoop;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

@ChannelHandler.Sharable
public class ReconnectHandler extends ChannelInboundHandlerAdapter {

    private final Logger logger = LoggerFactory.getLogger(ReconnectHandler.class);

    private int retries = 0;
    private RetryPolicyService retryPolicy;

    private LayRpcClientRegistry layRpcClientRegistry;

    public ReconnectHandler(LayRpcClientRegistry layRpcClientRegistry) {
        this.layRpcClientRegistry = layRpcClientRegistry;
        this.retryPolicy =  layRpcClientRegistry.getRetryPolicyService();
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        logger.info("Successfully established a connection to the server.");
        retries = 0;
        ctx.fireChannelActive();
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        String key=layRpcClientRegistry.getHost()+":"+layRpcClientRegistry.getPort();
        if (!LayRpcRegistyCache.serviceMap.containsKey(key)){
            LayRpcRegistryCache.removeChannel(ctx.channel());
            return;
        }
        if (retries == 0) {
            logger.warn("Lost the TCP connection with the server.");
        }
        boolean allowRetry = getRetryPolicy().allowRetry(retries);
        if (allowRetry) {
            long sleepTimeMs = getRetryPolicy().getSleepTimeMs(retries);
            logger.info(String.format("Try to reconnect to the server after %dms. Retry count: %d.", sleepTimeMs, ++retries));
            final EventLoop eventLoop = ctx.channel().eventLoop();
            eventLoop.schedule(() -> {
                logger.info("Reconnecting ...");
                //tcpClient.connect();
                LayRpcRegistryCache.removeChannel(layRpcClientRegistry.getChannel());
                layRpcClientRegistry.connect();
            }, sleepTimeMs, TimeUnit.MILLISECONDS);
        }
        logger.error("Reconnecting erro ...");
        ctx.fireChannelInactive();
    }
    private RetryPolicyService getRetryPolicy() {
        return this.retryPolicy;
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}
